#if !defined _WIN32
#define QT_CLEAN_NAMESPACE         /* avoid definition of INT32 and INT8 */
#endif

#include "View.h"

#include <QActionGroup>
#include <QApplication>
#include <QColorDialog>
#include <QCursor>
#include <QFileDialog>
#include <QFileInfo>
#include <QMdiSubWindow>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QRubberBand>
#include <QStyleFactory>
#include <QWheelEvent>
#include <Standard_WarningsDisable.hxx>
#if !defined(_WIN32) && (!defined(__APPLE__) || defined(MACOSX_USE_GLX)) && QT_VERSION < 0x050000
#include <QX11Info>
#endif
#include <Standard_WarningsRestore.hxx>
#include <Graphic3d_GraphicDriver.hxx>
#include <Graphic3d_TextureEnv.hxx>
#include <OpenGl_GraphicDriver.hxx>
#include <Geom_Axis2Placement.hxx>
#include <AIS_Trihedron.hxx>
#include <OcctWindow.h>
#include <Aspect_DisplayConnection.hxx>
#include <TopoDS_Shape.hxx>
#include <GC_MakeArcOfCircle.hxx>
#include <GC_MakeSegment.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Wire.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <TopoDS.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepFilletAPI_MakeFillet.hxx>
#include <TopExp_Explorer.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <Geom_Plane.hxx>
#include <BRepOffsetAPI_MakeThickSolid.hxx>
#include <BRepOffsetAPI_ThruSections.hxx>
#include <BRepLib.hxx>
#include <GCE2d_MakeSegment.hxx>
#include <Geom2d_Ellipse.hxx>
#include <Geom_CylindricalSurface.hxx>

namespace
{
    //! Map Qt buttons bitmask to virtual keys.
    Aspect_VKeyMouse qtMouseButtons2VKeys(Qt::MouseButtons theButtons)
    {
        Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
        if ((theButtons & Qt::LeftButton) != 0)
        {
            aButtons |= Aspect_VKeyMouse_LeftButton;
        }
        if ((theButtons & Qt::MiddleButton) != 0)
        {
            aButtons |= Aspect_VKeyMouse_MiddleButton;
        }
        if ((theButtons & Qt::RightButton) != 0)
        {
            aButtons |= Aspect_VKeyMouse_RightButton;
        }
        return aButtons;
    }

    //! Map Qt mouse modifiers bitmask to virtual keys.
    Aspect_VKeyFlags qtMouseModifiers2VKeys(Qt::KeyboardModifiers theModifiers)
    {
        Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
        if ((theModifiers & Qt::ShiftModifier) != 0)
        {
            aFlags |= Aspect_VKeyFlags_SHIFT;
        }
        if ((theModifiers & Qt::ControlModifier) != 0)
        {
            aFlags |= Aspect_VKeyFlags_CTRL;
        }
        if ((theModifiers & Qt::AltModifier) != 0)
        {
            aFlags |= Aspect_VKeyFlags_ALT;
        }
        return aFlags;
    }
}

static QCursor* defCursor = NULL;
static QCursor* handCursor = NULL;
static QCursor* panCursor = NULL;
static QCursor* globPanCursor = NULL;
static QCursor* zoomCursor = NULL;
static QCursor* rotCursor = NULL;

View::View(const std::vector<Handle(AIS_InteractiveContext)>& all_contexts, QWidget* parent)
    : QWidget(parent),
    myIsRaytracing(false),
    myIsShadowsEnabled(true),
    myIsReflectionsEnabled(false),
    myIsAntialiasingEnabled(false),
    myViewActions(0),
    myRaytraceActions(0),
    myBackMenu(NULL),
    allContexts(all_contexts)
{
#if !defined(_WIN32) && (!defined(__APPLE__) || defined(MACOSX_USE_GLX)) && QT_VERSION < 0x050000
    XSynchronize(x11Info().display(), true);
#endif

    myCurZoom = 0;
    setAttribute(Qt::WA_PaintOnScreen);
    setAttribute(Qt::WA_NoSystemBackground);
    setAttribute(Qt::WA_NativeWindow);

    myDefaultGestures = myMouseGestureMap;
    myCurrentMode = CurAction3d_Nothing;
    setMouseTracking(true);

    initViewActions();
    initCursors();

    setBackgroundRole(QPalette::NoRole);//NoBackground );
    // set focus policy to threat QContextMenuEvent from keyboard  
    setFocusPolicy(Qt::StrongFocus);
    init();

    InitSubview();

    // 坐标系
    myView->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_RED, 0.1, V3d_ZBUFFER);
    myView->SetBgGradientColors(Quantity_NOC_WHITE, Quantity_NOC_BLACK,
        Aspect_GradientFillMethod_Vertical);
    //myView->SetBackgroundColor(Quantity_NOC_DARKSLATEBLUE);
}

View::~View()
{
    delete myBackMenu;
}

void View::init()
{
    if (myView.IsNull())
        myView = allContexts[0]->CurrentViewer()->CreateView();

    Handle(OcctWindow) hWnd = new OcctWindow(this);
    myView->SetWindow(hWnd);
    if (!hWnd->IsMapped())
    {
        hWnd->Map();
    }
    myView->SetBackgroundColor(Quantity_NOC_BLACK);
    myView->MustBeResized();

    if (myIsRaytracing)
        myView->ChangeRenderingParams().Method = Graphic3d_RM_RAYTRACING;
}

void View::paintEvent(QPaintEvent*)
{
    //  QApplication::syncX();
    myView->InvalidateImmediate();
    //for (const auto& ctx : allContexts)
    //{
    //    FlushViewEvents(ctx, myView, true);
    //}


    FlushViewEvents(allContexts[0], myView, true);
    //FlushViewEvents(allContexts[1], myView, true);

    //subView->InvalidateImmediate();
    //FlushViewEvents(myContext, subView, true);
}

void View::resizeEvent(QResizeEvent*)
{
    //  QApplication::syncX();
    if (!myView.IsNull())
    {
        const Handle(Aspect_Window)& occ_window = myView->Window();
        occ_window->DoResize();

        myView->MustBeResized();
    }

    if (!subView.IsNull())
    {
        subView->MustBeResized();
    }
}

void View::OnSelectionChanged(const Handle(AIS_InteractiveContext)&,
    const Handle(V3d_View)&)
{
    emit selectionChanged();
}

void View::fitAll()
{
    myView->FitAll();
    myView->ZFitAll();
    myView->Redraw();
}

void View::fitArea()
{
    setCurrentAction(CurAction3d_WindowZooming);
}

void View::zoom()
{
    setCurrentAction(CurAction3d_DynamicZooming);
}

void View::pan()
{
    setCurrentAction(CurAction3d_DynamicPanning);
}

void View::rotation()
{
    setCurrentAction(CurAction3d_DynamicRotation);
}

void View::globalPan()
{
    // save the current zoom value
    myCurZoom = myView->Scale();
    // Do a Global Zoom
    myView->FitAll();
    // Set the mode
    setCurrentAction(CurAction3d_GlobalPanning);
}

void View::front()
{
    myView->SetProj(V3d_Yneg);
}

void View::back()
{
    myView->SetProj(V3d_Ypos);
}

void View::top()
{
    myView->SetProj(V3d_Zpos);
}

void View::bottom()
{
    myView->SetProj(V3d_Zneg);
}

void View::left()
{
    myView->SetProj(V3d_Xneg);
}

void View::right()
{
    myView->SetProj(V3d_Xpos);
}

void View::axo()
{
    myView->SetProj(V3d_XposYnegZpos);
}

void View::reset()
{
    myView->Reset();
}

void View::hlrOff()
{
    QApplication::setOverrideCursor(Qt::WaitCursor);
    myView->SetComputedMode(false);
    myView->Redraw();
    QApplication::restoreOverrideCursor();
}

void View::hlrOn()
{
    QApplication::setOverrideCursor(Qt::WaitCursor);
    myView->SetComputedMode(true);
    myView->Redraw();
    QApplication::restoreOverrideCursor();
}

void View::SetRaytracedShadows(bool theState)
{
    myView->ChangeRenderingParams().IsShadowEnabled = theState;

    myIsShadowsEnabled = theState;

    allContexts[0]->CurrentViewer()->Redraw();
    //myContext->UpdateCurrentViewer();
}

void View::SetRaytracedReflections(bool theState)
{
    myView->ChangeRenderingParams().IsReflectionEnabled = theState;

    myIsReflectionsEnabled = theState;

    allContexts[0]->CurrentViewer()->Redraw();
    //myContext->UpdateCurrentViewer();
}

void View::onRaytraceAction()
{
    QAction* aSentBy = (QAction*)sender();

    if (aSentBy == myRaytraceActions->at(ToolRaytracingId))
    {
        bool aState = myRaytraceActions->at(ToolRaytracingId)->isChecked();

        QApplication::setOverrideCursor(Qt::WaitCursor);
        if (aState)
            EnableRaytracing();
        else
            DisableRaytracing();
        QApplication::restoreOverrideCursor();
    }

    if (aSentBy == myRaytraceActions->at(ToolShadowsId))
    {
        bool aState = myRaytraceActions->at(ToolShadowsId)->isChecked();
        SetRaytracedShadows(aState);
    }

    if (aSentBy == myRaytraceActions->at(ToolReflectionsId))
    {
        bool aState = myRaytraceActions->at(ToolReflectionsId)->isChecked();
        SetRaytracedReflections(aState);
    }

    if (aSentBy == myRaytraceActions->at(ToolAntialiasingId))
    {
        bool aState = myRaytraceActions->at(ToolAntialiasingId)->isChecked();
        SetRaytracedAntialiasing(aState);
    }
}

void View::SetRaytracedAntialiasing(bool theState)
{
    myView->ChangeRenderingParams().IsAntialiasingEnabled = theState;

    myIsAntialiasingEnabled = theState;

    allContexts[0]->CurrentViewer()->Redraw();
    //myContext->UpdateCurrentViewer();
}

void View::EnableRaytracing()
{
    if (!myIsRaytracing)
        myView->ChangeRenderingParams().Method = Graphic3d_RM_RAYTRACING;

    myIsRaytracing = true;

    allContexts[0]->CurrentViewer()->Redraw();
    //myContext->UpdateCurrentViewer();
}

void View::DisableRaytracing()
{
    if (myIsRaytracing)
        myView->ChangeRenderingParams().Method = Graphic3d_RM_RASTERIZATION;

    myIsRaytracing = false;

    allContexts[0]->CurrentViewer()->Redraw();
    //myContext->UpdateCurrentViewer();
}

void View::updateToggled(bool isOn)
{
    QAction* sentBy = (QAction*)sender();

    if (!isOn)
        return;

    for (int i = ViewFitAllId; i < ViewHlrOffId; i++)
    {
        QAction* anAction = myViewActions->at(i);

        if ((anAction == myViewActions->at(ViewFitAreaId)) ||
            (anAction == myViewActions->at(ViewZoomId)) ||
            (anAction == myViewActions->at(ViewPanId)) ||
            (anAction == myViewActions->at(ViewGlobalPanId)) ||
            (anAction == myViewActions->at(ViewRotationId)))
        {
            if (anAction && (anAction != sentBy))
            {
                anAction->setCheckable(true);
                anAction->setChecked(false);
            }
            else
            {
                if (sentBy == myViewActions->at(ViewFitAreaId))
                    setCursor(*handCursor);
                else if (sentBy == myViewActions->at(ViewZoomId))
                    setCursor(*zoomCursor);
                else if (sentBy == myViewActions->at(ViewPanId))
                    setCursor(*panCursor);
                else if (sentBy == myViewActions->at(ViewGlobalPanId))
                    setCursor(*globPanCursor);
                else if (sentBy == myViewActions->at(ViewRotationId))
                    setCursor(*rotCursor);
                else
                    setCursor(*defCursor);

                sentBy->setCheckable(false);
            }
        }
    }
}

void View::initCursors()
{
    if (!defCursor)
        defCursor = new QCursor(Qt::ArrowCursor);
    if (!handCursor)
        handCursor = new QCursor(Qt::PointingHandCursor);
    if (!panCursor)
        panCursor = new QCursor(Qt::SizeAllCursor);
    if (!globPanCursor)
        globPanCursor = new QCursor(Qt::CrossCursor);
    //if ( !zoomCursor )
    //  zoomCursor = new QCursor( QPixmap( ApplicationCommonWindow::getResourceDir() + QString( "/" ) + QObject::tr( "ICON_CURSOR_ZOOM" ) ) );
    //if ( !rotCursor )
    //  rotCursor = new QCursor( QPixmap( ApplicationCommonWindow::getResourceDir() + QString( "/" ) + QObject::tr( "ICON_CURSOR_ROTATE" ) ) );
}

QList<QAction*>* View::getViewActions()
{
    initViewActions();
    return myViewActions;
}

QList<QAction*>* View::getRaytraceActions()
{
    initRaytraceActions();
    return myRaytraceActions;
}

/*!
  Get paint engine for the OpenGL viewer. [ virtual public ]
*/
QPaintEngine* View::paintEngine() const
{
    return 0;
}

void View::initViewActions()
{
    if (myViewActions)
        return;

    myViewActions = new QList<QAction*>();
    QString dir = QDir::currentPath();// ApplicationCommonWindow::getResourceDir() + QString("/");
    QAction* a;

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_FITALL")), QObject::tr("MNU_FITALL"), this);
    a->setToolTip(QObject::tr("TBR_FITALL"));
    a->setStatusTip(QObject::tr("TBR_FITALL"));
    connect(a, SIGNAL(triggered()), this, SLOT(fitAll()));
    myViewActions->insert(ViewFitAllId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_FITAREA")), QObject::tr("MNU_FITAREA"), this);
    a->setToolTip(QObject::tr("TBR_FITAREA"));
    a->setStatusTip(QObject::tr("TBR_FITAREA"));
    connect(a, SIGNAL(triggered()), this, SLOT(fitArea()));

    a->setCheckable(true);
    connect(a, SIGNAL(toggled(bool)), this, SLOT(updateToggled(bool)));
    myViewActions->insert(ViewFitAreaId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_ZOOM")), QObject::tr("MNU_ZOOM"), this);
    a->setToolTip(QObject::tr("TBR_ZOOM"));
    a->setStatusTip(QObject::tr("TBR_ZOOM"));
    connect(a, SIGNAL(triggered()), this, SLOT(zoom()));

    a->setCheckable(true);
    connect(a, SIGNAL(toggled(bool)), this, SLOT(updateToggled(bool)));
    myViewActions->insert(ViewZoomId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_PAN")), QObject::tr("MNU_PAN"), this);
    a->setToolTip(QObject::tr("TBR_PAN"));
    a->setStatusTip(QObject::tr("TBR_PAN"));
    connect(a, SIGNAL(triggered()), this, SLOT(pan()));

    a->setCheckable(true);
    connect(a, SIGNAL(toggled(bool)), this, SLOT(updateToggled(bool)));
    myViewActions->insert(ViewPanId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_GLOBALPAN")), QObject::tr("MNU_GLOBALPAN"), this);
    a->setToolTip(QObject::tr("TBR_GLOBALPAN"));
    a->setStatusTip(QObject::tr("TBR_GLOBALPAN"));
    connect(a, SIGNAL(triggered()), this, SLOT(globalPan()));

    a->setCheckable(true);
    connect(a, SIGNAL(toggled(bool)), this, SLOT(updateToggled(bool)));
    myViewActions->insert(ViewGlobalPanId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_FRONT")), QObject::tr("MNU_FRONT"), this);
    a->setToolTip(QObject::tr("TBR_FRONT"));
    a->setStatusTip(QObject::tr("TBR_FRONT"));
    connect(a, SIGNAL(triggered()), this, SLOT(front()));
    myViewActions->insert(ViewFrontId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_BACK")), QObject::tr("MNU_BACK"), this);
    a->setToolTip(QObject::tr("TBR_BACK"));
    a->setStatusTip(QObject::tr("TBR_BACK"));
    connect(a, SIGNAL(triggered()), this, SLOT(back()));
    myViewActions->insert(ViewBackId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_TOP")), QObject::tr("MNU_TOP"), this);
    a->setToolTip(QObject::tr("TBR_TOP"));
    a->setStatusTip(QObject::tr("TBR_TOP"));
    connect(a, SIGNAL(triggered()), this, SLOT(top()));
    myViewActions->insert(ViewTopId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_BOTTOM")), QObject::tr("MNU_BOTTOM"), this);
    a->setToolTip(QObject::tr("TBR_BOTTOM"));
    a->setStatusTip(QObject::tr("TBR_BOTTOM"));
    connect(a, SIGNAL(triggered()), this, SLOT(bottom()));
    myViewActions->insert(ViewBottomId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_LEFT")), QObject::tr("MNU_LEFT"), this);
    a->setToolTip(QObject::tr("TBR_LEFT"));
    a->setStatusTip(QObject::tr("TBR_LEFT"));
    connect(a, SIGNAL(triggered()), this, SLOT(left()));
    myViewActions->insert(ViewLeftId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_RIGHT")), QObject::tr("MNU_RIGHT"), this);
    a->setToolTip(QObject::tr("TBR_RIGHT"));
    a->setStatusTip(QObject::tr("TBR_RIGHT"));
    connect(a, SIGNAL(triggered()), this, SLOT(right()));
    myViewActions->insert(ViewRightId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_AXO")), QObject::tr("MNU_AXO"), this);
    a->setToolTip(QObject::tr("TBR_AXO"));
    a->setStatusTip(QObject::tr("TBR_AXO"));
    connect(a, SIGNAL(triggered()), this, SLOT(axo()));
    myViewActions->insert(ViewAxoId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_ROTATION")), QObject::tr("MNU_ROTATION"), this);
    a->setToolTip(QObject::tr("TBR_ROTATION"));
    a->setStatusTip(QObject::tr("TBR_ROTATION"));
    connect(a, SIGNAL(triggered()), this, SLOT(rotation()));
    a->setCheckable(true);
    connect(a, SIGNAL(toggled(bool)), this, SLOT(updateToggled(bool)));
    myViewActions->insert(ViewRotationId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_RESET")), QObject::tr("MNU_RESET"), this);
    a->setToolTip(QObject::tr("TBR_RESET"));
    a->setStatusTip(QObject::tr("TBR_RESET"));
    connect(a, SIGNAL(triggered()), this, SLOT(reset()));
    myViewActions->insert(ViewResetId, a);

    QActionGroup* ag = new QActionGroup(this);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_HLROFF")), QObject::tr("MNU_HLROFF"), this);
    a->setToolTip(QObject::tr("TBR_HLROFF"));
    a->setStatusTip(QObject::tr("TBR_HLROFF"));
    connect(a, SIGNAL(triggered()), this, SLOT(hlrOff()));
    a->setCheckable(true);
    ag->addAction(a);
    myViewActions->insert(ViewHlrOffId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_VIEW_HLRON")), QObject::tr("MNU_HLRON"), this);
    a->setToolTip(QObject::tr("TBR_HLRON"));
    a->setStatusTip(QObject::tr("TBR_HLRON"));
    connect(a, SIGNAL(triggered()), this, SLOT(hlrOn()));

    a->setCheckable(true);
    ag->addAction(a);
    myViewActions->insert(ViewHlrOnId, a);
}

void View::initRaytraceActions()
{
    if (myRaytraceActions)
        return;

    myRaytraceActions = new QList<QAction*>();
    QString dir = QDir::currentPath();// ApplicationCommonWindow::getResourceDir() + QString("/");
    QAction* a;

    a = new QAction(QPixmap(dir + QObject::tr("ICON_TOOL_RAYTRACING")), QObject::tr("MNU_TOOL_RAYTRACING"), this);
    a->setToolTip(QObject::tr("TBR_TOOL_RAYTRACING"));
    a->setStatusTip(QObject::tr("TBR_TOOL_RAYTRACING"));
    a->setCheckable(true);
    a->setChecked(false);
    connect(a, SIGNAL(triggered()), this, SLOT(onRaytraceAction()));
    myRaytraceActions->insert(ToolRaytracingId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_TOOL_SHADOWS")), QObject::tr("MNU_TOOL_SHADOWS"), this);
    a->setToolTip(QObject::tr("TBR_TOOL_SHADOWS"));
    a->setStatusTip(QObject::tr("TBR_TOOL_SHADOWS"));
    a->setCheckable(true);
    a->setChecked(true);
    connect(a, SIGNAL(triggered()), this, SLOT(onRaytraceAction()));
    myRaytraceActions->insert(ToolShadowsId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_TOOL_REFLECTIONS")), QObject::tr("MNU_TOOL_REFLECTIONS"), this);
    a->setToolTip(QObject::tr("TBR_TOOL_REFLECTIONS"));
    a->setStatusTip(QObject::tr("TBR_TOOL_REFLECTIONS"));
    a->setCheckable(true);
    a->setChecked(false);
    connect(a, SIGNAL(triggered()), this, SLOT(onRaytraceAction()));
    myRaytraceActions->insert(ToolReflectionsId, a);

    a = new QAction(QPixmap(dir + QObject::tr("ICON_TOOL_ANTIALIASING")), QObject::tr("MNU_TOOL_ANTIALIASING"), this);
    a->setToolTip(QObject::tr("TBR_TOOL_ANTIALIASING"));
    a->setStatusTip(QObject::tr("TBR_TOOL_ANTIALIASING"));
    a->setCheckable(true);
    a->setChecked(false);
    connect(a, SIGNAL(triggered()), this, SLOT(onRaytraceAction()));
    myRaytraceActions->insert(ToolAntialiasingId, a);
}

void View::InitSubview()
{
    Handle(OcctWindow) hWnd = new OcctWindow(this);
    myView->SetWindow(hWnd);

    const Handle(Aspect_Window)& the_window = myView->Window();

    Handle(V3d_View) view = GetView();
    Handle(V3d_Viewer) viewer = view->Viewer();

    V3d_TypeOfView view_type = V3d_ORTHOGRAPHIC; // 正交投影
    subView = new V3d_View(viewer, view_type);
    //Graphic3d_Vec2d sub_size(0.5, 1);
    //subView->SetWindow(view, sub_size, Aspect_TOTP_RIGHT_UPPER);
    //subView->SetWindow(the_window);
}

QPoint View::ScaleToOccPos(const QPoint& mouse_pos) const
{
    qreal scale_factor = QGuiApplication::primaryScreen()->devicePixelRatio();
    return scale_factor * mouse_pos;
}
QPointF View::ScaleToOccPos(const QPointF& mouse_pos) const
{
    qreal scale_factor = QGuiApplication::primaryScreen()->devicePixelRatio();
    return scale_factor * mouse_pos;
}

void View::activateCursor(const CurrentAction3d mode)
{
    switch (mode)
    {
    case CurAction3d_DynamicPanning:
        setCursor(*panCursor);
        break;
    case CurAction3d_DynamicZooming:
        setCursor(*zoomCursor);
        break;
    case CurAction3d_DynamicRotation:
        setCursor(*rotCursor);
        break;
    case CurAction3d_GlobalPanning:
        setCursor(*globPanCursor);
        break;
    case CurAction3d_WindowZooming:
        setCursor(*handCursor);
        break;
    case CurAction3d_Nothing:
    default:
        setCursor(*defCursor);
        break;
    }
}

void View::mousePressEvent(QMouseEvent* theEvent)
{
    QPoint scale_pos = ScaleToOccPos(theEvent->pos());

    const Graphic3d_Vec2i aPnt(scale_pos.x(), scale_pos.y());
    const Aspect_VKeyFlags aFlags = qtMouseModifiers2VKeys(theEvent->modifiers());
    if (!myView.IsNull()
        && UpdateMouseButtons(aPnt,
            qtMouseButtons2VKeys(theEvent->buttons()),
            aFlags,
            false))
    {
        updateView();
    }
    myClickPos = aPnt;
}

void View::mouseReleaseEvent(QMouseEvent* theEvent)
{
    QPoint scale_pos = ScaleToOccPos(theEvent->pos());

    const Graphic3d_Vec2i aPnt(scale_pos.x(), scale_pos.y());
    const Aspect_VKeyFlags aFlags = qtMouseModifiers2VKeys(theEvent->modifiers());
    if (!myView.IsNull()
        && UpdateMouseButtons(aPnt,
            qtMouseButtons2VKeys(theEvent->buttons()),
            aFlags,
            false))
    {
        updateView();
    }

    if (myCurrentMode == CurAction3d_GlobalPanning)
    {
        myView->Place(aPnt.x(), aPnt.y(), myCurZoom);

        subView->Place(aPnt.x(), aPnt.y(), myCurZoom);
    }
    if (myCurrentMode != CurAction3d_Nothing)
    {
        setCurrentAction(CurAction3d_Nothing);
    }
    if (theEvent->button() == Qt::RightButton
        && (aFlags & Aspect_VKeyFlags_CTRL) == 0
        && (myClickPos - aPnt).cwiseAbs().maxComp() <= 4)
    {
        Popup(aPnt.x(), aPnt.y());
    }
}

void View::mouseMoveEvent(QMouseEvent* theEvent)
{
    QPoint scale_pos = ScaleToOccPos(theEvent->pos());

    const Graphic3d_Vec2i aNewPos(scale_pos.x(), scale_pos.y());
    if (!myView.IsNull()
        && UpdateMousePosition(aNewPos,
            qtMouseButtons2VKeys(theEvent->buttons()),
            qtMouseModifiers2VKeys(theEvent->modifiers()),
            false))
    {
        updateView();
    }
}

//==============================================================================
//function : wheelEvent
//purpose  :
//==============================================================================
void View::wheelEvent(QWheelEvent* theEvent)
{
    QPointF scale_pos = ScaleToOccPos(theEvent->globalPosition());
    const Graphic3d_Vec2i aPos(scale_pos.x(), scale_pos.y());
    if (!myView.IsNull()
        && UpdateZoom(Aspect_ScrollDelta(aPos, theEvent->angleDelta().y() / 8)))
    {
        updateView();
    }
}

// =======================================================================
// function : updateView
// purpose  :
// =======================================================================
void View::updateView()
{
    update();
}

void View::defineMouseGestures()
{
    myMouseGestureMap.Clear();
    AIS_MouseGesture aRot = AIS_MouseGesture_RotateOrbit;
    activateCursor(myCurrentMode);
    switch (myCurrentMode)
    {
    case CurAction3d_Nothing:
    {
        noActiveActions();
        myMouseGestureMap = myDefaultGestures;
        break;
    }
    case CurAction3d_DynamicZooming:
    {
        myMouseGestureMap.Bind(Aspect_VKeyMouse_LeftButton, AIS_MouseGesture_Zoom);
        break;
    }
    case CurAction3d_GlobalPanning:
    {
        break;
    }
    case CurAction3d_WindowZooming:
    {
        myMouseGestureMap.Bind(Aspect_VKeyMouse_LeftButton, AIS_MouseGesture_ZoomWindow);
        break;
    }
    case CurAction3d_DynamicPanning:
    {
        myMouseGestureMap.Bind(Aspect_VKeyMouse_LeftButton, AIS_MouseGesture_Pan);
        break;
    }
    case CurAction3d_DynamicRotation:
    {
        myMouseGestureMap.Bind(Aspect_VKeyMouse_LeftButton, aRot);
        break;
    }
    }
}

void View::Popup(const int /*x*/, const int /*y*/)
{
#if 0
    ApplicationCommonWindow* stApp = ApplicationCommonWindow::getApplication();
    QMdiArea* ws = ApplicationCommonWindow::getWorkspace();
    QMdiSubWindow* w = ws->activeSubWindow();
    if (myContext->NbSelected())
    {
        QList<QAction*>* aList = stApp->getToolActions();
        QMenu* myToolMenu = new QMenu(0);
        myToolMenu->addAction(aList->at(ApplicationCommonWindow::ToolWireframeId));
        myToolMenu->addAction(aList->at(ApplicationCommonWindow::ToolShadingId));
        myToolMenu->addAction(aList->at(ApplicationCommonWindow::ToolColorId));

        QMenu* myMaterMenu = new QMenu(myToolMenu);

        QList<QAction*>* aMeterActions = ApplicationCommonWindow::getApplication()->getMaterialActions();

        QString dir = ApplicationCommonWindow::getResourceDir() + QString("/");
        myMaterMenu = myToolMenu->addMenu(QPixmap(dir + QObject::tr("ICON_TOOL_MATER")), QObject::tr("MNU_MATER"));
        for (int i = 0; i < aMeterActions->size(); i++)
            myMaterMenu->addAction(aMeterActions->at(i));

        myToolMenu->addAction(aList->at(ApplicationCommonWindow::ToolTransparencyId));
        myToolMenu->addAction(aList->at(ApplicationCommonWindow::ToolDeleteId));
        addItemInPopup(myToolMenu);
        myToolMenu->exec(QCursor::pos());
        delete myToolMenu;
    }
    else
    {
        if (!myBackMenu)
        {
            myBackMenu = new QMenu(0);

            QAction* a = new QAction(QObject::tr("MNU_CH_BACK"), this);
            a->setToolTip(QObject::tr("TBR_CH_BACK"));
            connect(a, SIGNAL(triggered()), this, SLOT(onBackground()));
            myBackMenu->addAction(a);
            addItemInPopup(myBackMenu);

            a = new QAction(QObject::tr("MNU_CH_ENV_MAP"), this);
            a->setToolTip(QObject::tr("TBR_CH_ENV_MAP"));
            connect(a, SIGNAL(triggered()), this, SLOT(onEnvironmentMap()));
            a->setCheckable(true);
            a->setChecked(false);
            myBackMenu->addAction(a);
            addItemInPopup(myBackMenu);
        }

        myBackMenu->exec(QCursor::pos());
    }
    if (w)
        w->setFocus();
#endif
}

void View::addItemInPopup(QMenu* /*theMenu*/)
{
}

void View::noActiveActions()
{
    for (int i = ViewFitAllId; i < ViewHlrOffId; i++)
    {
        QAction* anAction = myViewActions->at(i);
        if ((anAction == myViewActions->at(ViewFitAreaId)) ||
            (anAction == myViewActions->at(ViewZoomId)) ||
            (anAction == myViewActions->at(ViewPanId)) ||
            (anAction == myViewActions->at(ViewGlobalPanId)) ||
            (anAction == myViewActions->at(ViewRotationId)))
        {
            setCursor(*defCursor);
            anAction->setCheckable(true);
            anAction->setChecked(false);
        }
    }
}

void View::onBackground()
{
    QColor aColor;
    Standard_Real R1;
    Standard_Real G1;
    Standard_Real B1;
    myView->BackgroundColor(Quantity_TOC_RGB, R1, G1, B1);
    aColor.setRgb((Standard_Integer)(R1 * 255), (Standard_Integer)(G1 * 255), (Standard_Integer)(B1 * 255));

    QColor aRetColor = QColorDialog::getColor(aColor);

    if (aRetColor.isValid())
    {
        R1 = aRetColor.red() / 255.;
        G1 = aRetColor.green() / 255.;
        B1 = aRetColor.blue() / 255.;
        myView->SetBackgroundColor(Quantity_TOC_RGB, R1, G1, B1);
    }
    myView->Redraw();
}

void View::onEnvironmentMap()
{
    if (myBackMenu->actions().at(1)->isChecked())
    {
        QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
            tr("All Image Files (*.bmp *.gif *.jpg *.jpeg *.png *.tga)"));

        const TCollection_AsciiString anUtf8Path(fileName.toUtf8().data());

        Handle(Graphic3d_TextureEnv) aTexture = new Graphic3d_TextureEnv(anUtf8Path);

        myView->SetTextureEnv(aTexture);
    }
    else
    {
        myView->SetTextureEnv(Handle(Graphic3d_TextureEnv)());
    }

    myView->Redraw();
}

bool View::dump(Standard_CString theFile)
{
    return myView->Dump(theFile);
}

Handle(V3d_View)& View::getView()
{
    return myView;
}

View::CurrentAction3d View::getCurrentMode()
{
    return myCurrentMode;
}

// viewer helper
Handle(V3d_Viewer) ViewHelper::InitViewer()
{
    Handle(Aspect_DisplayConnection) aDisplayConnection;
#if !defined(_WIN32) && !defined(__WIN32__) && (!defined(__APPLE__) || defined(MACOSX_USE_GLX))
    aDisplayConnection = new Aspect_DisplayConnection(OSD_Environment("DISPLAY").Value());
#endif
    static Handle(OpenGl_GraphicDriver) graph_driver = new OpenGl_GraphicDriver(aDisplayConnection);

    Handle(V3d_Viewer) viewer = new V3d_Viewer(graph_driver);

    // 默认是 Graphic3d_TypeOfShadingModel_Phong
    viewer->SetDefaultShadingModel(Graphic3d_TypeOfShadingModel_Phong);
    //viewer->SetDefaultShadingModel(Graphic3d_TOSM_UNLIT);

    const double default_view_size = 1000.0;
    const V3d_TypeOfOrientation default_orient = V3d_XposYnegZpos;

    viewer->SetDefaultViewSize(default_view_size);
    viewer->SetDefaultViewProj(default_orient);
    viewer->SetComputedMode(true);
    viewer->SetDefaultComputedMode(true);

    viewer->SetDefaultLights();
    viewer->SetLightOn();
    return viewer;
}

Handle(AIS_InteractiveContext) ViewHelper::InitContext(Handle(V3d_Viewer) viewer)
{
    Handle(AIS_InteractiveContext) context = new AIS_InteractiveContext(viewer);

    const Handle(Prs3d_Drawer)& default_drawer = context->DefaultDrawer();
    
    default_drawer->SetMaximalChordialDeviation(0.1);
    //context->SetDefaultDrawer(default_drawer);

    context->SetDeviationCoefficient(0.001);

    context->SetAutoActivateSelection(Standard_True);
    context->SetAutomaticHilight(Standard_True);

    //context->SetDeviationCoefficient(0.001 * 0.01);

    // 显示坐标系
    Handle(Geom_Axis2Placement) placement = new Geom_Axis2Placement(gp::Origin(), gp::DZ(), gp::DX());
    Handle(AIS_Trihedron) trihedron = new AIS_Trihedron(placement);
    trihedron->SetXAxisColor(Quantity_NOC_RED);
    trihedron->SetYAxisColor(Quantity_NOC_GREEN);
    context->Display(trihedron, AIS_DisplayMode::AIS_Shaded, -1, true);

    // 显示模式
    //context->SetDisplayMode(0, false); // WireFrame
    context->SetDisplayMode(1, false); // Shaded
    //context->SetDisplayMode(2, false); // box
    //context->SetDisplayMode(3, false); // Wireframe with hidden line
    //context->SetDisplayMode(5, false); // Wireframe with hidden line

    // 显示参数线数量
    context->SetIsoNumber(5);

    //Handle(Prs3d_LineAspect) hidden_line_aspect = new Prs3d_LineAspect(Quantity_NOC_GRAY, Aspect_TOL_DASH, 1);
    //context->SetHiddenLineAspect(hidden_line_aspect);
    //context->DisableDrawHiddenLine();

    context->DefaultDrawer()->SetFaceBoundaryDraw(true); // 显示边界

    return context;
}

TopoDS_Shape ViewHelper::MakeBottle(const Standard_Real myWidth, const Standard_Real myHeight,
    const Standard_Real myThickness)
{
    // Profile : Define Support Points
    gp_Pnt aPnt1(-myWidth / 2., 0, 0);
    gp_Pnt aPnt2(-myWidth / 2., -myThickness / 4., 0);
    gp_Pnt aPnt3(0, -myThickness / 2., 0);
    gp_Pnt aPnt4(myWidth / 2., -myThickness / 4., 0);
    gp_Pnt aPnt5(myWidth / 2., 0, 0);

    // Profile : Define the Geometry
    Handle(Geom_TrimmedCurve) anArcOfCircle = GC_MakeArcOfCircle(aPnt2, aPnt3, aPnt4);
    Handle(Geom_TrimmedCurve) aSegment1 = GC_MakeSegment(aPnt1, aPnt2);
    Handle(Geom_TrimmedCurve) aSegment2 = GC_MakeSegment(aPnt4, aPnt5);

    // Profile : Define the Topology
    TopoDS_Edge anEdge1 = BRepBuilderAPI_MakeEdge(aSegment1);
    TopoDS_Edge anEdge2 = BRepBuilderAPI_MakeEdge(anArcOfCircle);
    TopoDS_Edge anEdge3 = BRepBuilderAPI_MakeEdge(aSegment2);
    TopoDS_Wire aWire = BRepBuilderAPI_MakeWire(anEdge1, anEdge2, anEdge3);

    // Complete Profile
    gp_Ax1 xAxis = gp::OX();
    gp_Trsf aTrsf;

    aTrsf.SetMirror(xAxis);
    BRepBuilderAPI_Transform aBRepTrsf(aWire, aTrsf);
    TopoDS_Shape aMirroredShape = aBRepTrsf.Shape();
    TopoDS_Wire aMirroredWire = TopoDS::Wire(aMirroredShape);

    BRepBuilderAPI_MakeWire mkWire;
    mkWire.Add(aWire);
    mkWire.Add(aMirroredWire);
    TopoDS_Wire myWireProfile = mkWire.Wire();

    // Body : Prism the Profile
    TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile);
    gp_Vec aPrismVec(0, 0, myHeight);
    TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile, aPrismVec);

    // Body : Apply Fillets
    BRepFilletAPI_MakeFillet mkFillet(myBody);
    TopExp_Explorer anEdgeExplorer(myBody, TopAbs_EDGE);
    while (anEdgeExplorer.More()) {
        TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExplorer.Current());
        //Add edge to fillet algorithm
        mkFillet.Add(myThickness / 12., anEdge);
        anEdgeExplorer.Next();
    }

    myBody = mkFillet.Shape();

    // Body : Add the Neck	
    gp_Pnt neckLocation(0, 0, myHeight);
    gp_Dir neckAxis = gp::DZ();
    gp_Ax2 neckAx2(neckLocation, neckAxis);

    Standard_Real myNeckRadius = myThickness / 4.;
    Standard_Real myNeckHeight = myHeight / 10.;

    BRepPrimAPI_MakeCylinder MKCylinder(neckAx2, myNeckRadius, myNeckHeight);
    TopoDS_Shape myNeck = MKCylinder.Shape();

    myBody = BRepAlgoAPI_Fuse(myBody, myNeck);

    // Body : Create a Hollowed Solid
    TopoDS_Face   faceToRemove;
    Standard_Real zMax = -1;

    for (TopExp_Explorer aFaceExplorer(myBody, TopAbs_FACE); aFaceExplorer.More(); aFaceExplorer.Next()) {
        TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current());
        // Check if <aFace> is the top face of the bottle抯 neck 
        Handle(Geom_Surface) aSurface = BRep_Tool::Surface(aFace);
        if (aSurface->DynamicType() == STANDARD_TYPE(Geom_Plane)) {
            Handle(Geom_Plane) aPlane = Handle(Geom_Plane)::DownCast(aSurface);
            gp_Pnt aPnt = aPlane->Location();
            Standard_Real aZ = aPnt.Z();
            if (aZ > zMax) {
                zMax = aZ;
                faceToRemove = aFace;
            }
        }
    }

    TopTools_ListOfShape facesToRemove;
    facesToRemove.Append(faceToRemove);
    BRepOffsetAPI_MakeThickSolid aSolidMaker;
    aSolidMaker.MakeThickSolidByJoin(myBody, facesToRemove, -myThickness / 50, 1.e-3);
    myBody = aSolidMaker.Shape();
    // Threading : Create Surfaces
    Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 0.99);
    Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 1.05);

    // Threading : Define 2D Curves
    gp_Pnt2d aPnt(2. * M_PI, myNeckHeight / 2.);
    gp_Dir2d aDir(2. * M_PI, myNeckHeight / 4.);
    gp_Ax2d anAx2d(aPnt, aDir);

    Standard_Real aMajor = 2. * M_PI;
    Standard_Real aMinor = myNeckHeight / 10;

    Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor);
    Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor / 4);
    Handle(Geom2d_TrimmedCurve) anArc1 = new Geom2d_TrimmedCurve(anEllipse1, 0, M_PI);
    Handle(Geom2d_TrimmedCurve) anArc2 = new Geom2d_TrimmedCurve(anEllipse2, 0, M_PI);
    gp_Pnt2d anEllipsePnt1 = anEllipse1->Value(0);
    gp_Pnt2d anEllipsePnt2 = anEllipse1->Value(M_PI);

    Handle(Geom2d_TrimmedCurve) aSegment = GCE2d_MakeSegment(anEllipsePnt1, anEllipsePnt2);
    // Threading : Build Edges and Wires
    TopoDS_Edge anEdge1OnSurf1 = BRepBuilderAPI_MakeEdge(anArc1, aCyl1);
    TopoDS_Edge anEdge2OnSurf1 = BRepBuilderAPI_MakeEdge(aSegment, aCyl1);
    TopoDS_Edge anEdge1OnSurf2 = BRepBuilderAPI_MakeEdge(anArc2, aCyl2);
    TopoDS_Edge anEdge2OnSurf2 = BRepBuilderAPI_MakeEdge(aSegment, aCyl2);
    TopoDS_Wire threadingWire1 = BRepBuilderAPI_MakeWire(anEdge1OnSurf1, anEdge2OnSurf1);
    TopoDS_Wire threadingWire2 = BRepBuilderAPI_MakeWire(anEdge1OnSurf2, anEdge2OnSurf2);
    BRepLib::BuildCurves3d(threadingWire1);
    BRepLib::BuildCurves3d(threadingWire2);

    // Create Threading 
    BRepOffsetAPI_ThruSections aTool(Standard_True);
    aTool.AddWire(threadingWire1);
    aTool.AddWire(threadingWire2);
    aTool.CheckCompatibility(Standard_False);

    TopoDS_Shape myThreading = aTool.Shape();

    // Building the Resulting Compound 
    TopoDS_Compound aRes;
    BRep_Builder aBuilder;
    aBuilder.MakeCompound(aRes);
    aBuilder.Add(aRes, myBody);
    aBuilder.Add(aRes, myThreading);

    return aRes;
}

